import axios, {AxiosRequestConfig, AxiosPromise, AxiosResponse} from 'axios';
import {delay} from 'various-untils';

const Delay: (timeout: number) => Promise<any> = delay;

export interface IAxiosRequestPromise<T> extends AxiosPromise<T> {
    cancel?: Function
}

export interface IAxiosRequestExtendConfig extends AxiosRequestConfig {
    delay?: number,
}

const AxiosRequestList: Array<any> = [];

class AxiosRequestBase {
    public static Options: AxiosRequestConfig;

    private axiosInstance = axios.create();
    private readonly requestOptions: AxiosRequestConfig;
    private readonly defaultOptions = {
        timeout: 60000
    };

    constructor(options?: AxiosRequestConfig) {
        this.requestOptions = {
            ...this.defaultOptions,
            ...AxiosRequestBase.Options,
            ...options,
        };
    }


    public async fetch<T = any>(url: string, extendOptions: AxiosRequestConfig): Promise<AxiosResponse<T>> {
        let promise = this.axiosInstance({
            ...this.requestOptions,
            ...extendOptions
        });


        try {
            AxiosRequestList.push(promise);
            await promise;
        } catch (e) {

        }

        let index = AxiosRequestList.indexOf(promise);
        if (index >= 0) {
            AxiosRequestList.splice(index, 1);
        }

        return promise;
    }
}

export function installAxios<T>(
    options: AxiosRequestConfig = {},
    authorityOptions: T
) {
    AxiosRequestBase.Options = {
        ...options,
    };

    if (authorityOptions) {
        let origin = AxiosRequestBase.Options.headers;

        AxiosRequestBase.Options.headers = {
            ...origin,
            ...authorityOptions,
        };
    }
}

export function request<T = any>(url: string, options: IAxiosRequestExtendConfig = {}): IAxiosRequestPromise<T> {
    let baseOptions: AxiosRequestConfig = options;
    let CancelToken = axios.CancelToken;
    let source = CancelToken.source();

    let _axiosRequestBase = new AxiosRequestBase({
        url: url,
        ...baseOptions,
        cancelToken: source.token
    });

    let promise: IAxiosRequestPromise<T>;

    if (options.delay !== null && options.delay !== undefined && options.delay >= 0) {
        promise = Delay(options.delay).then(() => _axiosRequestBase.fetch<T>(url, options));
    } else {
        promise = _axiosRequestBase.fetch<T>(url, options);
    }

    promise.cancel = function () {
        source.cancel('aborted');
        return true;
    };

    return promise;
}

export function abortEveryRequest() {
    if (AxiosRequestList.length > 0) {
        AxiosRequestList
            .splice(0, AxiosRequestList.length)
            .forEach((x: IAxiosRequestPromise<any>) => {
                if (x.cancel instanceof Function) {
                    x.cancel();
                }
            });
    }
}